package com.bw.config.security;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.collection.SpliteratorUtil;
import cn.hutool.core.text.split.SplitIter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.bw.entity.SysMenu;
import com.bw.service.MenuService;
import com.bw.service.UserService;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * 当有请求访问时，通过对比系统资源权限列表和用户资源权限列表（在用户登录时添加到用户信息中）来判断用户是否有该url的访问权限。
 * SecurityMetadataSource接口负责提供受保护对象所需要的权限。由于该案例中，受保护对象所需要的权限保存在数据库中，所以可以通过自定义类继承自
 * FilterInvocationSecurityMetadataSource，并重写getAttributes方法来提供受保护对象所需要的权限。
 */
@Slf4j
@Component
public class RoleMenuMetadataSource  implements FilterInvocationSecurityMetadataSource {
    @Autowired
    MenuService menuService;

    AntPathMatcher antPathMatcher = new AntPathMatcher();


    /**
     * 在基于URL地址的权限控制中，受保护对象就是FilterInvocation。
     * @param object 受保护对象
     * @return 受保护对象所需要的权限
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        // 从受保护对象FilterInvocation中提取出当前请求的URI地址，例如/admin/hello
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();

//        for (String s : WhiteURLConstant.whiteList) {
//            if (antPathMatcher.match(s, requestURI)) {
//                return SecurityConfig.createList("anonymous");
//            }
//        }
        // 查询所有菜单数据(每条数据中都包含访问该条记录所需要的权限)
        List<SysMenu> allMenu = menuService.getAllMenu();

        // 遍历菜单数据，如果当前请求的URL地址和菜单中某一条记录的pattern属性匹配上了(例如/admin/hello匹配上/admin/**)
        // 那么就可以获取当前请求所需要的权限；如果没有匹配上，则返回null。需要注意的是，如果AbstractSecurityInterceptor
        // 中的rejectPublicInvocations属性为false(默认值)时，则表示当getAttributes返回null时，允许访问受保护对象
        Set<String> needRoleSet = Sets.newHashSet();
        for (SysMenu menu : allMenu) {
            List<String> roleList = menu.getRoleList();
            String url = menu.getUrl();
            // 获取按钮或菜单下的URL
            List<String> urlList = StrUtil.split(url, ";",true,true);
            for (String u : urlList) {
                if (antPathMatcher.match(u, requestURI)) {
                    needRoleSet.addAll(roleList);
                }
            }
        }
        if (CollectionUtil.isNotEmpty(needRoleSet)){
            String[] roles = ArrayUtil.toArray(needRoleSet,String.class);
            log.info("url【{}】 -> 匹配到 {} 角色权限",requestURI,roles);
            return SecurityConfig.createList(roles);
        }

        // 默认需要管理员权限
        return SecurityConfig.createList("admin");
    }


    /**
     * 方便在项目启动阶段做校验，如果不需要校验，则直接返回null即可。
     * @return 所有的权限属性
     */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    /**
     * 表示当前对象支持处理的受保护对象是FilterInvocation。
     */
    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}


